package common.fattree;


import ap.BDDACLWrapper;
import common.*;
import common.countClass.CIBLine;
import common.countClass.firstCountLine;

import java.util.*;

//Aggregation  Core: 上游有多个，但每次计算只需要一个上游的数据到之后，计算后就可以直接发送，下游数量有多个，但要根据是哪个上游发的，来对应到哪几个下游 -- 计算
public class DynamicForwardingNode extends Node{
    //dst,tablePort
    HashMap<String,HashMap<String, Vector<Announcement>>> cibInMapping;  //dst(反向计数的起点，为了记录packetSpace），来自哪个dst的从哪个结点发送过来的announcements
    //dst,previousTablePort
    HashMap<String,HashMap<String, CIBIO>> cibInRecord;
    //dst,CIB
    HashMap<String,HashMap<Integer, CIBLine>> cibMapping; //dst(反向计数的起点，为了记录packetSpace），收到CIBIn后更新CIB，即记录哪个dst对应哪个CIB，更新计数从该CIB开始
    //dst name，packetSpace
    HashMap<String,Integer> dstPacketSpace;
    //for merge:dst,OldCIBOut
    HashMap<String,HashSet<Announcement>> dstOldCIBOut;
    //for check if the same as last time
    HashMap<String,CIBIO> dstIniCo;
    //for counting at first time
    HashMap<String,HashMap<Integer, CIBLine>> dstOldCIB;
    HashMap<String,HashMap<CIBLine,Vector<firstCountLine>>> dstCibHis;

    public Map<String, List<NodePointer>> forwardTable;
    //send
    HashMap<String,HashSet<String>> dstSrc;


    public DynamicForwardingNode(Device d, int index){
        super(d,index);
        cibInMapping=new HashMap<>();
        cibMapping=new HashMap<>();
        cibInRecord=new HashMap<>();
        dstPacketSpace=new HashMap<>();
        dstOldCIBOut=new HashMap<>();
        dstIniCo=new HashMap<>();
        dstOldCIB=new HashMap<>();
        dstCibHis=new HashMap<>();
        dstSrc=new HashMap<>();
    }

    public void deviceDone(String srcPort, CIBIO ci, String id) {
        if(dstSrc.get(ci.dst)==null){
            HashSet<String> src=new HashSet<>();
            src.add(srcPort);
            dstSrc.put(ci.dst,src);
        }else
        {
            dstSrc.get(ci.dst).add(srcPort);
        }
        recordPacketForHistory(srcPort,ci);
        updateCountCib(srcPort,ci);
        d.addRecord(id, index, "*cib", cibMapping.get(ci.dst).size());

        if(second){
            Result(ci.dst,id,srcPort);  //三项
        }else
        {
            Result(ci,id,srcPort);  //四项
        }
    }

    //dst no port   src+dst
    public void recordPacketForHistory(String srcPort,CIBIO ci){
        HashMap<String, CIBIO> previous=new HashMap<>();
        previous.put(srcPort,ci);
        HashMap<String, Vector<Announcement>> table=new HashMap<>();
        table.put(srcPort,ci.announcements);

        if(cibInRecord.get(ci.dst)==null ||cibInRecord.get(ci.dst).get(srcPort)==null){
            second=false; //初次到达
        }else
        {
            second=true;
        }

        if(second) {
            if (ci.equals(cibInRecord.get(ci.dst).get(srcPort))) {   //第二次收到相同的数据包
                return;
            }else
            {
                cibInRecord.get(ci.dst).put(srcPort,ci);

                //更新tp，保持最新---  保证存储的是下游的完整的CIB
                Vector<Announcement> oldAn=new Vector<>(cibInMapping.get(ci.dst).get(srcPort));
                for(Announcement i:oldAn){
                    if(ci.withdraw.contains(i.id)){
                        cibInMapping.get(ci.dst).get(srcPort).remove(i);  //移除变化的
                    }
                }
                cibInMapping.get(ci.dst).get(srcPort).addAll(ci.announcements); //新的

                //收到变化的cibIn开始更新：incremental update
                HashMap<Integer,CIBLine> newC=new HashMap<>(CIBUpdate(cibMapping.get(ci.dst),srcPort, ci));
                cibMapping.put(ci.dst,newC);   //第二次收到变化的table，所以要更新

            }
        }

        if(cibInRecord.get(ci.dst)==null){
            cibInRecord.put(ci.dst,previous);
            cibInMapping.put(ci.dst,table);
            dstPacketSpace.put(ci.dst,ci.packetSpace);  //初始化发送四项，packetSpace默认不变
        }else{
            if(cibInRecord.get(ci.dst).get(srcPort)==null){
                cibInRecord.get(ci.dst).put(srcPort,ci);
            }
            if(cibInMapping.get(ci.dst).get(srcPort)==null) {
                cibInMapping.get(ci.dst).put(srcPort, ci.announcements);
            }
        }
    }

    //计算第一次，初始化
    public void updateCountCib(String srcPort,CIBIO ci) {   //计算第一次
        if (!second) {
            if(cibMapping.get(ci.dst)==null) {          //初始化CIB
                initializeCIB(dstPacketSpace.get(ci.dst),ci);
            }

            HashMap<CIBLine,Vector<firstCountLine>> ch=new HashMap<>(countOneByOne(dstOldCIB.get(ci.dst),dstCibHis.get(ci.dst),srcPort, ci));


            HashMap<Integer,CIBLine> ic=new HashMap<>(updateCIBFromCibHis(cibMapping.get(ci.dst),dstOldCIB.get(ci.dst),ch));
            cibMapping.put(ci.dst,ic);    //FIB--CIB--cibHis--CIB CIBOut
            CIBIO co=new CIBIO();
            dstIniCo.put(ci.dst,co);
        }
    }

    public void run(){
        synchronized (bdd){
            updateNodeToFib();
        }

        runDevice();
    }

    //本地LEC变化
    public void getChangeToUpdateCIB(List<Change> changeList, String id){
        synchronized (bdd){
            updateNodeToFib();   //保证每次的needNext是最新的 todo 调用  id发送多次
        }

        HashSet<String> dsSet=new HashSet<>(cibMapping.keySet());
        for(String ds:dsSet) {
            HashMap<Integer, CIBLine> newC=new HashMap<>(changeToCIB(changeList,dstPacketSpace.get(ds),cibInMapping.get(ds),cibMapping.get(ds)));
            cibMapping.put(ds,newC);
            for(String src:dstSrc.get(ds))
                Result(ds,id,src);
        }
    }


    public void Result(String dst,String id,String srcPort){
//        System.out.println(cibOut);

        //全null不发
        if(cibOut.withdraw.size()==0&& cibOut.announcements.size()==0){
            d.println(d.name+index+" 0 both null");
            d.addRecord(id, index, "*finish", System.nanoTime());
            return;
        }

        //更新CIB 原本要发送的要撤回了
        Vector<Integer> del=new Vector<>();
        Vector<Announcement> oldAn = new Vector<>(cibOut.announcements);
        for (Announcement i : oldAn) {
            if (cibOut.withdraw.contains(i.id)) {
                cibOut.announcements.remove(i);
                del.add(i.id);
            }
        }
        if(del.size()>0){
            for(Integer i:del) {
                cibOut.withdraw.remove(i);
            }
        }

        //merge
        HashSet<Announcement> oc=new HashSet<>(mergeCib(dstOldCIBOut.get(dst)));
        dstOldCIBOut.put(dst,oc);

        //merge后，全null不发
        if(cibOut.withdraw.size()==0&& cibOut.announcements.size()==0){
            d.println(d.name+index+" 1 both null");
            d.addRecord(id, index, "*finish", System.nanoTime());
            return;
        }

        cibOut.dst=dst; //发三项
        CIBIO sendCIB=new CIBIO(cibOut.announcements,cibOut.withdraw,cibOut.dst);
        //System.out.println(sendCIB);
        if (!dstIniCo.get(dst).equals(cibOut)) {  //收齐了应该收的数据包计算后+与上一回发送计数结果不同 == 可以发送
            sendCount(sendCIB, forwardTable.get(srcPort), id);
            d.addRecord(id, index, "*count send", System.nanoTime());
            dstIniCo.get(dst).announcements.clear();
            dstIniCo.get(dst).withdraw.clear();
            dstIniCo.get(dst).announcements.addAll(cibOut.announcements);
            dstIniCo.get(dst).withdraw.addAll(cibOut.withdraw);
            dstIniCo.get(dst).dst=dst;
            cibOut.announcements.clear();
            cibOut.withdraw.clear();
        } else {
            d.println(d.name + index + "与上次计数结果一致，本次不再发送");
            if(ifEnd||second)
                d.addRecord(id, index, "*finish", System.nanoTime());
        }
    }


    public void Result(CIBIO ci,String id,String srcPort){
//        System.out.println(cibOut);

        //全null不发
        if(cibOut.withdraw.size()==0&& cibOut.announcements.size()==0){
            d.println(d.name+index+" 0 both null");
            d.addRecord(id, index, "*finish", System.nanoTime());
            return;
        }

        //更新CIB 原本要发送的要撤回了
        Vector<Integer> del=new Vector<>();
        Vector<Announcement> oldAn = new Vector<>(cibOut.announcements);
        for (Announcement i : oldAn) {
            if (cibOut.withdraw.contains(i.id)) {
                cibOut.announcements.remove(i);
                del.add(i.id);
            }
        }
        if(del.size()>0){
            for(Integer i:del) {
                cibOut.withdraw.remove(i);
            }
        }

        //merge
        HashSet<Announcement> oc=new HashSet<>(mergeCib(dstOldCIBOut.get(ci.dst)));
        dstOldCIBOut.put(ci.dst,oc);

        //merge后，全null不发
        if(cibOut.withdraw.size()==0&& cibOut.announcements.size()==0){
            d.println(d.name+index+" 1 both null");
            d.addRecord(id, index, "*finish", System.nanoTime());
            return;
        }

        cibOut.dst=ci.dst; //发四项
        CIBIO sendCIB=new CIBIO(cibOut.announcements,cibOut.withdraw,cibOut.dst,ci.packetSpace);
        //System.out.println(sendCIB);
        if (!dstIniCo.get(ci.dst).equals(cibOut)) {  //收齐了应该收的数据包计算后+与上一回发送计数结果不同 == 可以发送
            sendCount(sendCIB, forwardTable.get(srcPort), id);
            d.addRecord(id, index, "*count send", System.nanoTime());
            dstIniCo.get(ci.dst).announcements.clear();
            dstIniCo.get(ci.dst).withdraw.clear();
            dstIniCo.get(ci.dst).announcements.addAll(cibOut.announcements);
            dstIniCo.get(ci.dst).withdraw.addAll(cibOut.withdraw);
            dstIniCo.get(ci.dst).dst=ci.dst;
            cibOut.announcements.clear();
            cibOut.withdraw.clear();
        } else {
            d.println(d.name + index + "与上次计数结果一致，本次不再发送");
            if(ifEnd||second)
                d.addRecord(id, index, "*finish", System.nanoTime());
        }
    }


    public void initializeCIB(int packetSpace,CIBIO ci){

        int dropId=-1;
        HashMap<Integer,CIBLine> oldCIBdst=new HashMap<>();
        HashMap<Integer,CIBLine> CIBdst=new HashMap<>();
        HashMap<CIBLine,Vector<firstCountLine>> cibHisdst=new HashMap<>();


        for(FIB f:d.fibs) {
            int pre=0;
            synchronized (bdd) {
                pre = bdd.ref(bdd.and(f.predicate, packetSpace));  //f.predicate==BDDACLWrapper.BDDFalse：不做操作
            }
            //交集，加CIB--type+forward=drop history=null 0  size=0---drop
            if ( pre!= BDDACLWrapper.BDDFalse) {
                if(needNext.get(f).size()==0){    //drop：已经创建那就合并，否则创建
                    if(dropId>-1){
                        int orPre=0;
                        synchronized (bdd){
                            orPre=bdd.orTo(CIBdst.get(dropId).predicate,pre);
                        }
                        CIBdst.put(dropId,new CIBLine(dropId,orPre,"drop",new HashSet<>()));
                    }else
                    {
                        dropId=indexOfCib++;
                        CIBLine c = new CIBLine(dropId, pre, "drop", new HashSet<>());  //action为空的，history为空
                        CIBdst.put(c.id, c);     //初始化，count=[0] history=null，不需要参与计算的CIB

                    }

                }else {
                    CIBLine c = new CIBLine(indexOfCib++, pre, String.valueOf(f.type), needNext.get(f));
                    oldCIBdst.put(c.id, c);  //要参与计算的LEC，计算完成就更新到CIB中
                    HashSet<Integer> h = new HashSet<>();
                    h.add(c.id);

                    Vector<firstCountLine> result = new Vector<>();
                    firstCountLine al = new firstCountLine(c.id, c.predicate);//vector=[0]  history为null
                    synchronized (bdd) {
                        bdd.ref(c.predicate);
                    }
                    result.add(al);
                    cibHisdst.put(c, result);//每一条cib都非空
                }
            }
        }
        dstOldCIB.put(ci.dst,oldCIBdst);
        cibMapping.put(ci.dst,CIBdst);
        dstCibHis.put(ci.dst,cibHisdst);
    }


    public void setForwardTable(Map<String, List<NodePointer>> map){
        this.forwardTable = map;
    }
}
